package db

import (
	"errors"
	"fmt"
	"go.uber.org/zap"
	"gorm.io/driver/mysql"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"strings"
	"time"
)

const MysqlDriver = "mysql"
const SqliteDriver = "sqlite3"

func mysqlDial(dsn string) gorm.Dialector {
	return mysql.New(mysql.Config{
		DSN:                       dsn,
		DisableDatetimePrecision:  true,  // 禁用 datetime 精度，MySQL 5.6 之前的数据库不支持
		DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式，MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
		DontSupportRenameColumn:   true,  // 用 `change` 重命名列，MySQL 8 之前的数据库和 MariaDB 不支持重命名列
		SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
	})
}

type log struct {
	*zap.Logger
}

func (l *log) Printf(fm string, args ...interface{}) {
	l.Logger.Debug(fmt.Sprintf(fm, args...))
}

func NewGormDBWithLog(databaseUrl string, zapLog *zap.Logger, logLevel string) (*gorm.DB, error) {
	level := logger.Silent
	switch logLevel {
	case "error":
		level = logger.Error
	case "info":
		level = logger.Info
	case "warn":
		level = logger.Warn
	}
	if zapLog != nil {
		return newGormDB(databaseUrl, logger.New(&log{zapLog}, logger.Config{
			SlowThreshold:             200 * time.Millisecond,
			LogLevel:                  level,
			IgnoreRecordNotFoundError: false,
			Colorful:                  false,
		}))
	} else {
		return newGormDB(databaseUrl, logger.Default.LogMode(level))
	}
}

func NewGormDB(databaseUrl string) (*gorm.DB, error) {
	return newGormDB(databaseUrl, nil)
}

func newGormDB(databaseUrl string, log logger.Interface) (*gorm.DB, error) {
	url := strings.SplitN(databaseUrl, "://", 2)
	if len(url) != 2 || url[1] == "" {
		return nil, errors.New("database url is error")
	}
	var dail gorm.Dialector
	switch url[0] {
	case MysqlDriver:
		dail = mysqlDial(url[1])
		break
	case SqliteDriver:
		dail = sqlite.Open(url[1])
	default:
		return nil, errors.New("database url error")
	}

	//todo logger
	return gorm.Open(dail, &gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true,
		SkipDefaultTransaction:                   true,
		Logger:                                   log,
	})
}
